package asteroids.entity.weapon;

import java.util.ArrayList;

import asteroids.engine.GameState;
import asteroids.engine.Utils;
import asteroids.entity.GameObject;
import asteroids.entity.Icing;
import asteroids.entity.PlayerShip;
import asteroids.entity.Ship;
import asteroids.entity.equipment.Equipment;
import asteroids.factory.ShapeLib;

/**
 * Cruise missiles will target the nearest heat-generating target to try to kill
 * (not asteroids) They will fly straight if they have no viable target
 * 
 * @author whitecd1
 * 
 */
public class CruiseMissiles extends Weapon
{
    CruiseMissileControl control;

    // private static final int MISSILE_RELOAD_TIME =
    // Utils.MILLISECONDS_PER_SECOND / 3;
    private static final int MISSILE_REFIRE_DELAY = (int) ((18d * Utils.FRAMES_PER_SECOND) / 24d);
    private static final int MISSILE_ENERGY_USE = 8;
    private static final int MISSILE_TTL = 5 * Utils.FRAMES_PER_SECOND;
    private static final double MISSILE_POWER = 4;

    public CruiseMissiles(Ship ship)
    {
        super(ship, MISSILE_ENERGY_USE, MISSILE_REFIRE_DELAY);
        control = new CruiseMissileControl(ship);
        ship.equip(control); // enable the ship to control the missiles it
                             // fires.
    }

    @Override
    public boolean FIRE(GameState state)
    {
        if( canFireYet(state) )
        {
            manageFireControl(state);
            Utils.CartesianCoordinate spawnPoint = Utils.cartesianFromPolar(
                    ((PlayerShip) ship).getHardPoints()[0].rotateBy(ship.getRotation())).shiftBy(
                    ship.getXPosition(), ship.getYPosition());

            control.FIRE(new Missile(spawnPoint.getXComponent(), spawnPoint.getYComponent(), ship
                    .getRotation(), 0, MISSILE_POWER, MISSILE_TTL, ship));
        }
        return false;
    }
}

/**
 * The 'brains' behind a Missile. This class has access to the GameState, and
 * assigns targets for each missile. The missile steers itself.
 * 
 * @author whitecd1
 * 
 */
class CruiseMissileControl extends Equipment
{
    private ArrayList<Missile> missiles;

    public CruiseMissileControl(Ship ship)
    {
        super(ship);
        missiles = new ArrayList<Missile>();
    }

    public boolean hasLiveMissile()
    {
        return missiles.size() > 0;
    }

    public void FIRE(Missile missile)
    {
        missiles.add(missile);
        ship.add(missile);
    }

    @Override
    public void update(GameState state)
    {
        // get the point of interest - targets will be chosen with respect to
        // their distance from the point 3 shipLengths in front of the ship
        Utils.CartesianCoordinate reticule = null;
        Ship target = null;

        for( Missile m : missiles )
        {
            // make sure m has a target - could reassign too, if needed
            if( !m.hasTarget() )
            {
                if( reticule == null )
                { // only calculate the reticule's
                  // location if we actually need
                  // it.
                    reticule = Utils
                            .cartesianFromPolar(
                                    Utils.createPolarCoordinate(ship.getRotation(),
                                            ShapeLib.shipLength * 5)).shiftBy(ship.getXPosition(),
                                    ship.getYPosition());
                    // find the nearest target
                    ArrayList<Ship> possibleTargets = state.getShipManager().getObjects();
                    // Collections.sort(possibleTargets, new
                    // Utils.distanceComparator(reticule));
                    Utils.sortByDistance(possibleTargets, reticule);
                    // find the nearest enemy that is not ourself
                    // target = (possibleTargets.size() > 1) ?
                    // possibleTargets.get(1) : null;
                    int i = 0;
                    target = possibleTargets.get(i); // should be the owner
                    while (target.getAffiliation().equals(ship.getAffiliation())
                            && possibleTargets.size() > i)
                    {
                        target = possibleTargets.get(i);
                        i++;
                    }
                }
                // there may not be good target. Make sure we aren't still
                // targeting ourselves
                if( !ship.getAffiliation().equals(target.getAffiliation()) )
                {
                    m.setTarget(target);
                }
            }
        }
        super.update(state);
    }

    @Override
    public void respawn()
    {
        for( Missile missile : missiles )
        {
            missile.destroy();
        }
    }

}

/**
 * Lean, mean, ship-hunting machine. Makes no effort to avoid obstacles.
 * 
 * @author whitecd1
 * 
 */
class Missile extends Bullet
{
    private static final double MISSILE_SPEED = 240d / Utils.FRAMES_PER_SECOND;
    // private static final double acceleration = 500 / Utils.FRAMES_PER_SECOND
    // / Utils.FRAMES_PER_SECOND;
    private double turnSpeed = 90 / Utils.FRAMES_PER_SECOND;

    private static Utils.PolarCoordinate[] missileShape = {
            Utils.createPolarCoordinate(3, ShapeLib.shipLength / 2),
            Utils.createPolarCoordinate(12, ShapeLib.shipLength / 3 - 1),
            Utils.createPolarCoordinate(170, ShapeLib.shipLength / 3 - 1),
            Utils.createPolarCoordinate(165, ShapeLib.shipLength / 2),
            Utils.createPolarCoordinate(195, ShapeLib.shipLength / 2),
            Utils.createPolarCoordinate(190, ShapeLib.shipLength / 3 - 1),
            Utils.createPolarCoordinate(348, ShapeLib.shipLength / 3 - 1),
            Utils.createPolarCoordinate(357, ShapeLib.shipLength / 2) };
    private static Utils.PolarCoordinate exhaust = Utils.createPolarCoordinate(180,
            ShapeLib.shipLength / 2);
    private Ship target;
    private Ship owner;

    public Missile(double x, double y, double direction, double speed, double power,
            int timeToLive, Ship ship)
    {
        super(x, y, direction, speed, power, timeToLive, missileShape, ship);
        owner = ship;
    }

    @Override
    public void update()
    {
        if( target != null && target.isAlive() )
        {
            // figure out if we need to turn, and which way.
            double targetDir = Utils.pointDirection(x_position, y_position, target.getXPosition(),
                    target.getYPosition());
            while (rotation < 0)
            {
                rotation += 360;
            }
            double dirDiff = targetDir - rotation % 360;
            // logger.info("target: " + target.getName());
            // logger.info("targetDir: " + targetDir);
            // logger.info("rotation: " + rotation);
            // logger.info("dirDiff: " + dirDiff);
            if( Math.abs(dirDiff) < 10 )
            {
                // logger.info("S");
                // keep going straight
            }
            else if( dirDiff >= 180 )
            {
                // logger.info("L");
                rotation += -turnSpeed;
            }
            else if( dirDiff >= 0 && dirDiff < 180 )
            {
                // logger.info("R");
                rotation += turnSpeed;
            }
            else if( dirDiff >= -180 && dirDiff < 0 )
            {
                // logger.info("L");
                rotation += -turnSpeed;
            }
            else if( dirDiff < -180 )
            {
                // logger.info("R");
                rotation += turnSpeed;
            }
        }
        else
        {
            target = null;
        }

        // create missile trail
        Utils.CartesianCoordinate cartEx = Utils.cartesianFromPolar(exhaust.rotateBy(rotation))
                .shiftBy(x_position, y_position);
        icing.add(new Icing(cartEx.getXComponent(), cartEx.getYComponent(),
                (int) (Utils.FRAMES_PER_SECOND * 0.75)));

        Utils.CartesianCoordinate velocityVect = Utils.cartesianFromPolar(Utils
                .createPolarCoordinate(rotation, MISSILE_SPEED));
        x_speed = velocityVect.getXComponent();
        y_speed = velocityVect.getYComponent();
        super.update();
    }

    public boolean hasTarget()
    {
        if( target != null && target.isAlive() )
        {
            return true;
        }
        return false;
    }

    public void setTarget(Ship ship)
    {
        target = ship;
    }

    @Override
    public void collide(GameObject other, double power)
    {
        super.collide(other, power);
        if( !alive )
        {
            owner.add(new Explosion(x_position, y_position, max_radius * 3, false, owner));
        }
    }

    public String toString()
    {
        return "Missile";
    }
}